<?php
/**
 * Celebrio FileSystem drivers
 *
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage FileSystem\Drivers
 */
namespace Celebrio\FileSystem\Drivers;

use Nette\Environment;
use Nette\Object;

use Celebrio\FileSystem\Drivers\IDriver;

use Celebrio\FileSystem\File;
use Celebrio\FileSystem\FileException;
use Celebrio\FileSystem\Folder;
use Celebrio\FileSystem\DataFile;
use Celebrio\FileSystem\FolderNotFoundException;
use Celebrio\FileSystem\InvalidFilesystemOperation;
use Celebrio\FileSystem\FileNameCollisionException;

use InvalidArgumentException;

/**
 * Local FS module implements basic operations on local file system.
 *
 * @author     pavel, honza
 * @package    FileSystem
 */
class Local extends Object implements IDriver {

    const NAME = "local";

    /**
     * @var String FS root path (e.g /home/pavel/albireo/fsroot)
     */
    private static $rootPath = null;

    private function getRootPath() {
        if (self::$rootPath === null) {
            throw new InvalidStateException("Root path has not been set up yet.");
        }
        return self::$rootPath;
    }
    
    public static function setRootPath($path) {
        self::$rootPath = $path;
    }


    /* ------------------------------------------------------------- */
    /* ------------------------------------------------------------- */
    /* -----------------FILESYSTEM-METHODS-------------------------- */
    /* ------------------------------------------------------------- */
    /* ------------------------------------------------------------- */

    /**
     * Loads data from directory or file
     *
     * @param DataFile $dataFile the requested file to load
     * @return DataFile contents data from files or directory children
     */
    public function load(DataFile $dataFile, $stream = true) {
        $url = $this->getRootPath() . $dataFile->getUrl();
        if ($dataFile->isDirectory()) {
            //all requests on loading directory should have already been handled
            //by VFS. Therefore THIS SHOULD NEVER HAPPEN.

            throw new InvalidFilesystemOperation("Required load on the directory.");
        } else {
            //$h = fopen("/home/qwei/documents/albireo/fslocal/celebriofs/tahni/slozka/soubor.txt", "r");
            $file = new File($url . $dataFile->getName(), "r");
            
            if($stream) {
                $dataFile->setData($file->getAloneFileHandler());
            } else {
                $dataFile->setData($file->getContents());               
            }
            $dataFile->setSize($file->getFileSize()); 
        }
        return $dataFile;
    }

    /**
     * Save the incoming object. At first recursively creates the path
     * and then saves the file or directory based on mimetype.
     *
     * @param DataFile $dataFile the file to be saved
     */
    public function save(DataFile $dataFile) {
        $directoryUrl = $this->getRootPath() . $dataFile->getUrl();
        $url = $directoryUrl . $dataFile->getName();
        $folder = new Folder($directoryUrl, TRUE);

        if ($dataFile->isDirectory()) {
            $folder = new Folder($url, TRUE);
        } else {
            if(is_resource($dataFile->getData())) {
                $this->saveStream($dataFile);
                return true;
            }
            $file = new File($url, "w");
            $file->put($dataFile->getData());
            $file->closeFile();
        }
    }
    /**
     * This function saves data as stream. Parameter $dataFile has in attribute
     * $data stream_resource (file handler) and takes little part of data
     * from source (handler) and stores into new file.
     * 
     * IMPORTANT: This method closes the input stream (handler)
     * @param DataFile $datafile 
     */
    private function saveStream(DataFile $dataFile) {
        $directoryUrl = $this->getRootPath() . $dataFile->getUrl();
        $url = $directoryUrl . $dataFile->getName();
        $folder = new Folder($directoryUrl, TRUE);
        
        $handler = $dataFile->getData();
        
        $file = new File($url, "w");
        fseek($handler, 0);
        while (!feof($handler)) {
                    $data = fread($handler, 1 * (1024 * 1024));
                    $file->write($data);
                    ob_flush();
                    flush();
                }
        fclose($handler);
        $file->closeFile();
    }


    /**
     * Move the source to the destination folder.
     * We can put file into directory, replace the folder, replace the directory.
     * On directories this works recursivly (it moves all sub-directories).
     * This move works as rename so its more faster than creating and deleting
     *
     * @param DataFile $source the source file/directory (first parameter of mv)
     * @param DataFile $destination the target (second parameter of mv)
     * 
     * @throws FileException when try to put directory into file.
     */
    public function move(DataFile $source, DataFile $destination) {
        $urlSource = $this->getRootPath() . $source->getUrl();
        $urlDest = $this->getRootPath() . $destination->getUrl();
        if ($source->isDirectory()) {
            if ($destination->isDirectory()) {
                $sourceFolder = new Folder($urlSource . $source->getName());
                $sourceFolder->move($urlDest . $destination->getName());
                
            } else {
                throw new FileException("Can't move directory into file!");
            }
        } else {
            if ($destination->isDirectory()) {
                $folder = new Folder($urlDest . $destination->getName(), TRUE);
                $file = new File($urlSource . $source->getName());
                $file->move($folder->getFolderURL() . $source->getName());
                $file->closeFile();
            } else {
                $folder = new Folder($urlDest, TRUE);
                $file = new File($urlSource . $source->getName());
                $file->move($urlDest . $destination->getName());
                $file->closeFile();
            }
        }
    }

    /**
     * Delete File or folder specified by first parameter. Recursive delete
     * on directories can be required by second parameter. By default,
     * this method does NOT delete recursively so if the specified first
     * parameter is non-empty directory, the exception is thrown.
     *
     * @param DataFile $dataFile the file/directory we want to delete
     * @param bool $recursive specifies whether the method should delete
     *        the directory recursively (has no effect when deleting single file).
     */
    public function delete(DataFile $dataFile, $recursive = false) {
        $url = $this->getRootPath() . $dataFile->getUrl();

        if ($dataFile->isDirectory()) {
            $folder = new Folder($url . $dataFile->getName());
            if ($recursive) {
                $folder->delete(true);
            } else {
                if (!$folder->delete()) {
                    throw new InvalidFilesystemOperation("The non-empty directory can't be removed.");
                }
            }
        } else {
            $file = new File($url . $dataFile->getName());
            $file->delete();
        }
    }

    /**
     * Try if file or folder exists
     *
     * @param DataFile $file the file/dir to be checked whether it exists
     * 
     * @return bool TRUE when the file/dir exists, FALSE when it does not
     */
    public function exists(DataFile $file) {
        $url = $this->getRootPath() . $file->getUrl() . $file->getName();
        return \file_exists($url);
    }
    
    public function onChangeOwner(DataFile $file) {
        //Do nothing for this moment
    }

    
    /* --------------------- deprecated crap below ------------------- */
    
//    /**
//     * Copy the source to the destination folder.
//     * We can put file into directory, replace the folder, replace the directory.
//     * On directories this works recursivly (it copies all sub-directories).
//     *
//     * @param array $arguments DataFile $source, DataFile $destination
//     * @throws FileException when try to put directory into file.
//     *
//     * @deprecated handled by VFS using load and save methods
//     */
//    private function copy($arguments) {
//        $source = $arguments[0];
//        $destination = $arguments[1];
//        $urlSource = $this->getRootPath() . $source->getUrl();
//        $urlDest = $this->getRootPath() . $destination->getUrl();
//        if($mimeParser->isDirectory($source)) {
//            if($mimeParser->isDirectory($destination)) {
//                $folder = new Folder($urlSource . $source->getName());
//                $folder->copy($urlDest . $destination->getName());
//                //$folder = new Folder($urlDest . $destination->getName(), TRUE);
//            } else {
//                throw new FileException("Can't copy directory into file!");
//            }
//        } else {
//            if($mimeParser->isDirectory($destination)) {
//                $folder = new Folder($urlDest . $destination->getName(), TRUE);
//                $file = new File($urlSource . $source->getName());
//                $file->copy($folder->getFolderURL() . $source->getName());
//                $file->closeFile();
//            } else {
//                $folder = new Folder($urlDest, TRUE);
//                $file = new File($urlSource . $source->getName());
//                $file->copy($urlDest . $destination->getName());
//                $file->closeFile();
//            }
//        }
//    }
}